//
//  FFScrollView.swift
//  ZgwApp
//
//  Created by li on 2025-02-18.
//

import SwiftUI
/**
 https://github.com/bvankuik/UIViewRepresentableExamples/blob/master/UIViewRepresentableExamples/ScrollView/LegacyScrollView.swift
 上面的文章介绍了, 自定scrollview  如何实现 vertical,horizontal方案
 */


struct FFScrollView<Content: View>: View {
	
	@ObservedObject var viewModel: FFScrollViewModel
	
	@ViewBuilder
	let content: () -> Content
	
	var body: some View {
		FFScrollViewRepresentable(scrollView: viewModel.scrollView, content: content)
			.coordinateSpace(name: "FFScroll")
	}
}

@MainActor
class FFScrollViewModel: NSObject, ObservableObject {
	
	let scrollView: UIScrollView = UIScrollView()
	
	func configure(_ config: (_ objc: FFScrollViewModel) -> Void) -> FFScrollViewModel {
		
		config(self)
		return self
	}
}

private struct FFScrollContentView<Content: View>: View {
	
	@ViewBuilder
	var content: () -> Content
	var body: some View {
		ZStack {
			content()
		}
//		.scrollPosition(Binding<ScrollPosition>)
	}
}

struct FFScrollPosition {

	var contentOffset: CGPoint = .zero
	var animated: Bool = false
	
	init(_ point: CGPoint = .zero) {
		self.contentOffset = point
	}
	
	mutating func scrollTo(x: CGFloat, y: CGFloat, animated: Bool) {
//		scrollView?.setContentOffset(CGPoint(x: x, y: y), animated: animated)
		
		contentOffset = .init(x: x, y: y)
		self.animated = animated

//		scrollOffsetChangeCallback?(CGPoint(x: x, y: y))
	}
	
	var scrollOffsetChangeCallback: ((CGPoint, Bool) -> Void)?
	
}

private struct FFScrollViewRepresentable<Content: View>: UIViewRepresentable {
	var scrollView: UIScrollView?
	@ViewBuilder
	let content: () -> Content
	
	@Environment(\.ffScrollViewPagingEnabled) private var isPagingEnabled
	@Environment(\.ffScrollViewPhaseChange) private var scrollPhaseChange
	@Environment(\.ffScrollGeometryChange) private var scrollGeometryChange
	@State private var scrollPhase: FFScrollPhase = .idle
	
	func makeCoordinator() -> Coordinator {
		let coordinator = Coordinator()
		coordinatorCallback(coordinator: coordinator)
		return coordinator
	}

	func makeUIView(context: Context) -> UIScrollView {

		let view = scrollView ?? UIScrollView()
		view.delegate = context.coordinator

		let controller = UIHostingController(rootView: content())
		context.coordinator.controller = controller
		
		if let contentView = controller.view {
			contentView.backgroundColor = .clear
			view.addSubview(contentView)
		}

		return view
	}

	func updateUIView(_ uiView: UIScrollView, context: Context) {
		
		uiView.isDirectionalLockEnabled = true
		if let controller = context.coordinator.controller {
			controller.rootView = content()
			if let view = controller.view {
				view.sizeToFit()
				view.invalidateIntrinsicContentSize()
				let size = view.intrinsicContentSize
				uiView.contentSize = size

				context.coordinator.controller?.view.frame = CGRect(origin: .zero, size: size)
			}
		}
	}



	func scrollPhaseChangeContext(scrollView: UIScrollView) -> FFScrollPhaseChangeContext {
		let scrollGeometry = FFScrollGeometry(contentOffset: scrollView.contentOffset, contentSize: scrollView.contentSize, contentInsets: .init(top: scrollView.contentInset.top, leading: scrollView.contentInset.left, bottom: scrollView.contentInset.bottom, trailing: scrollView.contentInset.right))
		let context = FFScrollPhaseChangeContext(geomtery: scrollGeometry)
		return context
	}

	func coordinatorCallback(coordinator: Coordinator)  {
		coordinator.scrollViewWillBeginDragging = {
			scrollView in
			let oldValue = scrollPhase
			if scrollPhase != .tracking {
				scrollPhase = .tracking


				scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
			}


			let oldValue1 = scrollPhase
			if scrollPhase != .interacting {

				scrollPhase = .interacting
				scrollPhaseChange(oldValue1, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
			}
		}

		coordinator.scrollViewDidScroll = {
			scrollView in

			let proxy = FFScrollGeometry(contentOffset: scrollView.contentOffset, contentSize: scrollView.contentSize, contentInsets: .init(top: scrollView
				.contentInset.top, leading: scrollView.contentInset.left, bottom: scrollView.contentInset.bottom, trailing: scrollView.contentInset.right))


			scrollGeometryChange(proxy)
			if scrollView.contentOffset != scrollView.contentOffset {
//				scrollPositionOffset(scrollView.contentOffset)
			}

		}

		coordinator.scrollViewWillEndDragging = { scrollView, velocity, targetContentOffset in

		}

		coordinator.scrollViewDidEndDragging = { scrollView, decelerate in
			if !decelerate {
				let oldValue = scrollPhase
				if scrollPhase != .idle {
					scrollPhase = .idle
					scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
				}
			}
		}

		coordinator.scrollViewWillBeginDecelerating = {
			scrollView in
			let oldValue = scrollPhase
			if scrollPhase != .decelerating {
				scrollPhase = .decelerating
				scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
			}

		}

		coordinator.scrollViewDidEndScrollingAnimation = {
			scrollView in
			let oldValue = scrollPhase
			if scrollPhase != .animating {

				scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
//				scrollPhase = .animating
			}
		}

		coordinator.scrollViewDidEndDecelerating = {
			scrollView in
			let oldValue = scrollPhase
			if scrollPhase != .idle {
				scrollPhase = .idle
				scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
			}

		}
	}

	class Coordinator: NSObject, UIScrollViewDelegate {

//		var controller: UIHostingController<FFScrollContentView<Content>>?
		var controller: UIHostingController<Content>?

		var scrollViewWillBeginDragging: ((_ scrollView: UIScrollView) -> Void)?
		var scrollViewDidScroll: ((_ scrollView: UIScrollView) -> Void)?
		var scrollViewWillEndDragging: ((_ scrollView: UIScrollView, _ velocity: CGPoint, _ targetContentOffset: UnsafeMutablePointer<CGPoint>) -> Void)?
		var scrollViewDidEndDragging: ((_ scrollView: UIScrollView, _ decelerate: Bool) -> Void)?
		var scrollViewWillBeginDecelerating: ((_ scrollView: UIScrollView) -> Void)?
		var scrollViewDidEndScrollingAnimation: ((_ scrollView: UIScrollView) -> Void)?
		var scrollViewDidEndDecelerating: ((_ scrollView: UIScrollView) -> Void)?


		func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
			scrollViewWillBeginDragging?(scrollView)
		}

		func scrollViewDidScroll(_ scrollView: UIScrollView) {
			scrollViewDidScroll?(scrollView)
		}

		func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
			scrollViewWillEndDragging?(scrollView, velocity, targetContentOffset)
		}

		func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
			scrollViewDidEndDragging?(scrollView, decelerate)
		}

		func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
			scrollViewWillBeginDecelerating?(scrollView)
		}


		func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
			scrollViewDidEndScrollingAnimation?(scrollView)
		}

		func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
			scrollViewDidEndDecelerating?(scrollView)
		}
	}

	class FFScrollViewModel: NSObject, ObservableObject, UIScrollViewDelegate {
	

//		var controller: UIHostingController<FFScrollContentView<Content>>?
		var controller: UIHostingController<Content>?

		var scrollViewWillBeginDragging: ((_ scrollView: UIScrollView) -> Void)?
		var scrollViewDidScroll: ((_ scrollView: UIScrollView) -> Void)?
		var scrollViewWillEndDragging: ((_ scrollView: UIScrollView, _ velocity: CGPoint, _ targetContentOffset: UnsafeMutablePointer<CGPoint>) -> Void)?
		var scrollViewDidEndDragging: ((_ scrollView: UIScrollView, _ decelerate: Bool) -> Void)?
		var scrollViewWillBeginDecelerating: ((_ scrollView: UIScrollView) -> Void)?
		var scrollViewDidEndScrollingAnimation: ((_ scrollView: UIScrollView) -> Void)?
		var scrollViewDidEndDecelerating: ((_ scrollView: UIScrollView) -> Void)?


		func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
			scrollViewWillBeginDragging?(scrollView)
		}

		func scrollViewDidScroll(_ scrollView: UIScrollView) {
			scrollViewDidScroll?(scrollView)
		}

		func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
			scrollViewWillEndDragging?(scrollView, velocity, targetContentOffset)
		}

		func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
			scrollViewDidEndDragging?(scrollView, decelerate)
		}

		func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
			scrollViewWillBeginDecelerating?(scrollView)
		}


		func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
			scrollViewDidEndScrollingAnimation?(scrollView)
		}

		func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
			scrollViewDidEndDecelerating?(scrollView)
		}
	}
}


//private struct FFScrollViewRepresentable<Content: View>: UIViewRepresentable {
//	
////	var contentOffset: Binding<CGPoint>
//	@ViewBuilder
//	let content: () -> Content
//	
//	
//	@Environment(\.ffScrollViewPagingEnabled) private var isPagingEnabled
//	@Environment(\.ffScrollViewPhaseChange) private var scrollPhaseChange
//	@Environment(\.ffScrollGeometryChange) private var scrollGeometryChange
//	@Environment(\.ffScrollPosition) private var scrollPosition
//	@Environment(\.ffScrollPositionOffset) private var scrollPositionOffset
//	
//	
//	
//	@State private var scrollPhase: FFScrollPhase = .idle
//	
//	func makeCoordinator() -> Coordinator {
//		let coordinator = Coordinator()
//		coordinatorCallback(coordinator: coordinator)
//		return coordinator
//	}
//  
//	func makeUIView(context: Context) -> UIScrollView {
//		
//		let view = UIScrollView()
//		view.delegate = context.coordinator
//		
//		let controller = UIHostingController(rootView: content())
//		context.coordinator.controller = controller
//		if let contentView = controller.view {
//			contentView.backgroundColor = .clear
//			view.addSubview(contentView)
//		}
//		
////		scrollPosition.scrollOffsetChangeCallback = {
////			point, animated in
////			view.setContentOffset(point, animated: animated)
////		}
//		return view
//	}
//	
//	func updateUIView(_ uiView: UIScrollView, context: Context) {
////		uiView.setContentOffset(<#T##contentOffset: CGPoint##CGPoint#>, animated: <#T##Bool#>)
//		
//		
//		uiView.isPagingEnabled = isPagingEnabled
//		uiView.isDirectionalLockEnabled = true
//		if let controller = context.coordinator.controller {
////			controller.rootView = FFScrollContentView<Content>(content: content)
//			controller.rootView = content()
//			if let view = controller.view {
//				view.sizeToFit()
//				view.invalidateIntrinsicContentSize()
//				let size = view.intrinsicContentSize
//				uiView.contentSize = size
//				
//				context.coordinator.controller?.view.frame = CGRect(origin: .zero, size: size)
//			}
//		}
//		
////		let contentOffset = scrollPosition.contentOffset
//////		if scrollPosition.contentOffset !=  uiView.contentOffset {
////		if let contentOffset = scrollPosition.contentOffset {
////			uiView.setContentOffset(contentOffset, animated: scrollPosition.animated)
////		}
//			
////		}
//	}
//	
//	@ViewBuilder
//	func contentView() -> some View {
//		ZStack {
//			content()
//		}
//	}
//	
//	func scrollPhaseChangeContext(scrollView: UIScrollView) -> FFScrollPhaseChangeContext {
//		let scrollGeometry = FFScrollGeometry(contentOffset: scrollView.contentOffset, contentSize: scrollView.contentSize, contentInsets: .init(top: scrollView.contentInset.top, leading: scrollView.contentInset.left, bottom: scrollView.contentInset.bottom, trailing: scrollView.contentInset.right))
//		let context = FFScrollPhaseChangeContext(geomtery: scrollGeometry)
//		return context
//	}
//	
//	func coordinatorCallback(coordinator: Coordinator)  {
//		coordinator.scrollViewWillBeginDragging = {
//			scrollView in
//			let oldValue = scrollPhase
//			if scrollPhase != .tracking {
//				scrollPhase = .tracking
//				
//				
//				scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
//			}
//			
//			
//			let oldValue1 = scrollPhase
//			if scrollPhase != .interacting {
//				
//				scrollPhase = .interacting
//				scrollPhaseChange(oldValue1, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
//			}
//		}
//		
//		coordinator.scrollViewDidScroll = {
//			scrollView in
//			
//			let proxy = FFScrollGeometry(contentOffset: scrollView.contentOffset, contentSize: scrollView.contentSize, contentInsets: .init(top: scrollView
//				.contentInset.top, leading: scrollView.contentInset.left, bottom: scrollView.contentInset.bottom, trailing: scrollView.contentInset.right))
//			
//			
//			scrollGeometryChange(proxy)
//			if scrollView.contentOffset != scrollView.contentOffset {
////				scrollPositionOffset(scrollView.contentOffset)
//			}
//			
//		}
//		
//		coordinator.scrollViewWillEndDragging = { scrollView, velocity, targetContentOffset in
//		
//		}
//		
//		coordinator.scrollViewDidEndDragging = { scrollView, decelerate in
//			if !decelerate {
//				let oldValue = scrollPhase
//				if scrollPhase != .idle {
//					scrollPhase = .idle
//					scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
//				}
//			}
//		}
//		
//		coordinator.scrollViewWillBeginDecelerating = {
//			scrollView in
//			let oldValue = scrollPhase
//			if scrollPhase != .decelerating {
//				scrollPhase = .decelerating
//				scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
//			}
//
//		}
//		
//		coordinator.scrollViewDidEndScrollingAnimation = {
//			scrollView in
//			let oldValue = scrollPhase
//			if scrollPhase != .animating {
//				
//				scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
////				scrollPhase = .animating
//			}
//		}
//		
//		coordinator.scrollViewDidEndDecelerating = {
//			scrollView in
//			let oldValue = scrollPhase
//			if scrollPhase != .idle {
//				scrollPhase = .idle
//				scrollPhaseChange(oldValue, scrollPhase, scrollPhaseChangeContext(scrollView: scrollView))
//			}
//		
//		}
//	}
//	
//	class Coordinator: NSObject, UIScrollViewDelegate {
//		
////		var controller: UIHostingController<FFScrollContentView<Content>>?
//		var controller: UIHostingController<Content>?
//		
//		var scrollViewWillBeginDragging: ((_ scrollView: UIScrollView) -> Void)?
//		var scrollViewDidScroll: ((_ scrollView: UIScrollView) -> Void)?
//		var scrollViewWillEndDragging: ((_ scrollView: UIScrollView, _ velocity: CGPoint, _ targetContentOffset: UnsafeMutablePointer<CGPoint>) -> Void)?
//		var scrollViewDidEndDragging: ((_ scrollView: UIScrollView, _ decelerate: Bool) -> Void)?
//		var scrollViewWillBeginDecelerating: ((_ scrollView: UIScrollView) -> Void)?
//		var scrollViewDidEndScrollingAnimation: ((_ scrollView: UIScrollView) -> Void)?
//		var scrollViewDidEndDecelerating: ((_ scrollView: UIScrollView) -> Void)?
//
//
//		func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
//			scrollViewWillBeginDragging?(scrollView)
//		}
//		
//		func scrollViewDidScroll(_ scrollView: UIScrollView) {
//			scrollViewDidScroll?(scrollView)
//		}
//		
//		func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
//			scrollViewWillEndDragging?(scrollView, velocity, targetContentOffset)
//		}
//		
//		func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
//			scrollViewDidEndDragging?(scrollView, decelerate)
//		}
//		
//		func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
//			scrollViewWillBeginDecelerating?(scrollView)
//		}
//		
//		
//		func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
//			scrollViewDidEndScrollingAnimation?(scrollView)
//		}
//		
//		func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
//			scrollViewDidEndDecelerating?(scrollView)
//		}
//	}
//}

public enum FFScrollPhase: Hashable, Equatable, Sendable {
	
	case idle
	case tracking
	case interacting
	case decelerating
	case animating
	
	public var isScrolling: Bool {
		return self == .interacting || self == .decelerating || self == .animating
	}
}

public struct FFScrollPhaseChangeContext {
	
	init(geomtery: FFScrollGeometry, velocity: CGVector? = nil) {
		self._geomtery = geomtery
		self._velocity = velocity
	}

	public var geometry: FFScrollGeometry { _geomtery }

	fileprivate var _geomtery: FFScrollGeometry


	public var velocity: CGVector? {
		_velocity
	}
	
	fileprivate var _velocity: CGVector?
}

public struct FFScrollGeometry : Equatable, Sendable {
	
	public var contentOffset: CGPoint

	public var contentSize: CGSize

	public var contentInsets: EdgeInsets

}

struct FFScrollTestView: View {
	
	@State var count: Int = 0
	@ObservedObject  var viewModel  = FFScrollViewModel()
	var body: some View {
		
//		ScrollView {
//			ZStack {
//				Color.red
//					.frame(width: 1400, height: 1200)
//			}
//		}
//		.frame(alignment: .topLeading)
		
		FFScrollView(viewModel: viewModel, content: {
			
			HStack(spacing: 0) {
				ForEach(0..<3) { index in
					if index == 0 {
						Color.orange
							.frame(width: UIScreen.main.bounds.size.width, height: 1300)
					}else if index == 1 {
						ZStack {
							Color.gray
							Text("\(count)")
							ScrollView {
								ForEach(0..<300, content: { index in
									Text("\(index)")
										.frame(width: 300)
								})
							}
						}
						.frame(width: UIScreen.main.bounds.size.width, height: 300)
						.onTapGesture {
							count += 1
						}
					}else if index == 2 {
						
						Color.black
							.frame(width: UIScreen.main.bounds.size.width, height: 300)
					}
				}
			}
		})
		.ffScrollViewPagingEnabled(true)
		.onFFScrollPhaseChange { oldValue, newValue, context in
			print(oldValue, newValue)
		}
		.onFFScrollGeometryChange(for: CGFloat.self) { geomtery in
			return geomtery.contentOffset.x
		} action: { oldValue, newValue in
			print(oldValue, newValue)
		}

		
	}
}

#Preview {
	
	FFScrollTestView()
}
